package com.chen.recruit.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.recruit.common.api.CommonResult;
import com.chen.recruit.common.constants.MessageConstants;
import com.chen.recruit.common.enums.CommonStatus;
import com.chen.recruit.common.enums.MenuType;
import com.chen.recruit.common.utils.SysUtils;
import com.chen.recruit.domain.Menu;
import com.chen.recruit.domain.RoleMenu;
import com.chen.recruit.domain.UserRole;
import com.chen.recruit.domain.vo.RoleVo;
import com.chen.recruit.mapper.MenuMapper;
import com.chen.recruit.mapper.RoleMenuMapper;
import com.chen.recruit.mapper.UserRoleMapper;
import com.chen.recruit.service.RoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.chen.recruit.domain.Role;
import com.chen.recruit.mapper.RoleMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 角色Service业务层处理
 *
 * @author cgy
 * @date 2022-08-17
 */
@Service
@RequiredArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    private final MenuMapper menuMapper;
    private final UserRoleMapper userRoleMapper;
    private final RoleMenuMapper roleMenuMapper;
    private final SysUtils sysUtils;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult addOrUpdateRole(RoleVo vo) {
        if (checkRoleNameUnique(vo) || checkRoleCodeUnique(vo)) {
            return CommonResult.fail(MessageConstants.ROLE_NAME_OR_CODE_REPEAT);
        }
        if (checkRoleIsUse(vo.getId()) && vo.getStatus() == CommonStatus.DISABLE) {
            return CommonResult.fail(MessageConstants.UPDATE_ROLE_STATUS_FAIL);
        }

        Role role = BeanUtil.toBean(vo, Role.class);

        if (ObjectUtil.isNull(role.getId())) {
            if (!this.save(role)) {
                return CommonResult.fail();
            }
        } else {
            if (!this.updateById(role)) {
                return CommonResult.fail();
            }
        }

        List<Integer> menuIds = vo.getMenuIds();
        if (CollUtil.isNotEmpty(menuIds)) {
            roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, role.getId()));
            for (Integer menuId : menuIds) {
                int record = roleMenuMapper.insert(new RoleMenu(role.getId(), menuId));
                if (record != 1) {
                    return CommonResult.fail();
                }
            }
        }

        sysUtils.clearMenuCache();

        return CommonResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult deleteRoleByIds(Integer[] roleIds) {
        if (checkRoleIsUse(roleIds)) {
            return CommonResult.fail(MessageConstants.DELETE_ROLE_FAIL);
        }
        return baseMapper.deleteBatchIds(Arrays.asList(roleIds)) > 0 && roleMenuMapper
                .delete(Wrappers.<RoleMenu>lambdaQuery().in(RoleMenu::getRoleId, roleIds)) >= 0 ?
                CommonResult.success() : CommonResult.fail();
    }

    @Override
    public List<Role> getRolesByUserId(Object userId) {
        List<Integer> roleIds = userRoleMapper
                .selectList(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, userId))
                .stream().map(UserRole::getRoleId).collect(Collectors.toList());
        return baseMapper.selectBatchIds(roleIds);
    }

    @Override
    public List<Integer> getMenuIdsByRoleId(Integer roleId) {
        List<Integer> menuIds = roleMenuMapper
                .selectList(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, roleId))
                .stream().map(RoleMenu::getMenuId).collect(Collectors.toList());

        return menuMapper.selectBatchIds(menuIds)
                .stream().filter(menu -> menu.getType() != MenuType.DIRECTORY)
                .map(Menu::getId).collect(Collectors.toList());
    }

    private boolean checkRoleNameUnique(RoleVo vo) {
        return baseMapper.exists(Wrappers.<Role>lambdaQuery()
                .eq(Role::getName, vo.getName())
                .ne(ObjectUtil.isNotNull(vo.getId()), Role::getId, vo.getId()));
    }

    private boolean checkRoleCodeUnique(RoleVo vo) {
        return baseMapper.exists(Wrappers.<Role>lambdaQuery()
                .eq(Role::getCode, vo.getCode())
                .ne(ObjectUtil.isNotNull(vo.getId()), Role::getId, vo.getId()));
    }

    private boolean checkRoleIsUse(Integer... ids) {
        return userRoleMapper.exists(Wrappers.<UserRole>lambdaQuery().in(UserRole::getRoleId, ids));
    }

}
